package codeGeneration.bean;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import lexer.TokenConstant;

import util.Common;
import util.HeaderOfJavaFile;
import util.Relationship;

import codeGeneration.Generator;
import databaseTransaction.Table;
import databaseTransaction.TableField;

public abstract class ClassGenerator extends Generator {
	protected String className;
	protected Table table;
	protected String packageName;
	protected String fileLocation;
	protected StringBuffer sbuffWriter;
	protected HeaderOfJavaFile headerOfJavaFile;

	public ClassGenerator(Table table, String className) {
		this.table = table;
		this.className = className;

		sbuffWriter = new StringBuffer();
		indentOneTime(HeaderOfJavaFile.packageNamePlaceHolder, null, ";\n",
				sbuffWriter, true);
		indentOneTime(HeaderOfJavaFile.importsListPlaceHolder, null, "",
				sbuffWriter, true);
		headerOfJavaFile = new HeaderOfJavaFile();
	}

	public void setClassName(String className) {
		this.className = className;
	}

	public String getClassName() {
		return this.className;
	}

	public void setTable(Table table) {
		this.table = table;
	}

	public Table getTable() {
		return this.table;
	}

	public void generateClass() throws IOException {
		generateDeclare();
		generateMethod();
		decreaseIndentOneTime("}", sbuffWriter);

		replacePlaceHolder(sbuffWriter, headerOfJavaFile);

		FileWriter fileWriter = null;
		try {
			fileWriter = getFileWriter(getFileName());
			fileWriter.append(sbuffWriter);
		} catch (IOException e) {
			e.printStackTrace();
			throw e;
		}

		if (fileWriter != null)
			closeFileWriter(fileWriter);
	}

	public String getFileName() {
		return fileLocation + '/' + this.className + ".java";
	}

	public void generateSetMethod(String name, String type) {
		String methodName = name.substring(0, 1).toUpperCase()
				+ name.substring(1);
		appendToFile(sbuffWriter, "\n");
		indentOneTime("public void set" + methodName + "(" + type + " " + name
				+ ")", null, "{", sbuffWriter, false);

		appendToFile(sbuffWriter, "this." + name + " = " + name + ";\n");
		decreaseIndentOneTime("}", sbuffWriter);

	}

	public void generateGetMethod(String name, String type) {
		String methodName = Common.convertFirstLetterToUpperCase(name);
		appendToFile(sbuffWriter, "\n");
		indentOneTime("public " + type + " get" + methodName + "()", null, "{",
				sbuffWriter, false);

		appendToFile(sbuffWriter, "return this." + name + ";\n");
		decreaseIndentOneTime("}", sbuffWriter);
	}

	public void generateMethod() {
		List<TableField> allOfField = new ArrayList<TableField>(table.getId());
		allOfField.addAll(table.getTableFields());

		String fieldName;
		String fieldType;

		for (TableField tblField : allOfField) {
			fieldName = tblField.getName();
			fieldType = tblField.parseType();

			if (this instanceof JDBCClassGenerator) {
				generateSetMethod(fieldName, fieldType);
				generateGetMethod(fieldName, fieldType);

				continue;
			}

			else if (tblField.getRelationship() != null) {
				List<Object> extra = tblField.getExtra();
				if (extra != null && extra.size() != 0
						&& extra.contains(TokenConstant.PK)) {
					if (tblField.getName().toLowerCase().endsWith("id")) {
						fieldName = tblField.getName();
						fieldType = tblField.parseType();

						generateSetMethod(fieldName, fieldType);
						generateGetMethod(fieldName, fieldType);
					}
				}

				Relationship relationship = tblField.getRelationship();
				switch (relationship.getRelationship()) {
				case TokenConstant.MANYTOONE:
					fieldType = relationship.getTableName();
					fieldName = Common.convertFirstLetterToLowerCase(fieldType);
					break;
				case TokenConstant.ONETOMANY:
					fieldType = "Set<" + relationship.getTableName() + '>';
					fieldName = Common
							.convertFirstLetterToLowerCase(relationship
									.getTableName()) + 's';
					break;
				case TokenConstant.MANYTOMANY:
					fieldType = "Set<" + relationship.getOtherTableName() + '>';
					fieldName = Common
							.convertFirstLetterToLowerCase(relationship
									.getOtherTableName()) + 's';
					break;
				case TokenConstant.ONETOONE:
					break;
				}
			}
			generateSetMethod(fieldName, fieldType);
			generateGetMethod(fieldName, fieldType);
		}
	}

	public abstract void generateDeclare();
}